use std::{collections::HashSet,  sync::Arc};


use chat_core::{Chat, Message};
use futures::StreamExt;
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgListener;
use anyhow::Result;
use tracing::{info, warn};

use crate::AppState;

#[derive(Debug,Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum AppEvent {
    NewChat(Chat),
    AddToChat(Chat),
    RemoveFromChat(Chat),
    NewMessage(Message),
}

#[derive(Debug)]
struct Nofification {
    //users being impacted,,so we should send the event to them
    users_ids: HashSet<u64>,
    event: Arc<AppEvent>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ChatUpdate{
    op: String,
    old: Option<Chat>,
    new: Option<Chat>
}

#[derive(Debug, Serialize, Deserialize)]
struct ChatMessageCreated {
    message: Message,
    members: Vec<i64>,
}

pub async fn setup_pg_listener(state: AppState) -> anyhow::Result<()> {
    let mut listener =
        PgListener::connect(&state.config.server.db_url).await?;
    listener.listen("chat_updated").await?;
    listener.listen("chat_message_created").await?;

    let mut stream = listener.into_stream();

    tokio::spawn(async move {
        while let Some(Ok(notif)) = stream.next().await {
            info!("Received notification: {:?}", notif);
            let users = &state.users;
            let notiication = Nofification::load(notif.channel(), notif.payload())?;
            for user_id in notiication.users_ids {
                if let Some(tx) = users.get(&user_id){
                    if let Err(e) = tx.send(notiication.event.clone()){
                        warn!("Failed to send notification to user {}: {:?}", user_id, e);
                    }
                }
                
            }
        }
        Ok::<_, anyhow::Error>(())
    });

    Ok(())
}

impl Nofification {
    fn load(r#type: &str, payload: &str) -> Result<Self> {
        match r#type {
            "chat_updated" =>{
                let payload: ChatUpdate = serde_json::from_str(payload)?;
                let user_ids =
                get_affected_user_ids(payload.old.as_ref(), payload.new.as_ref());
                let event = match payload.op.as_str() {
                    "INSERT" => AppEvent::NewChat(payload.new.unwrap()),
                    "UPDATE" => AppEvent::AddToChat(payload.new.unwrap()),
                    "DELETE" => AppEvent::RemoveFromChat(payload.old.unwrap()),
                    _ => return Err(anyhow::anyhow!("Invalid operation")),
                };
                Ok(Self{
                    users_ids: user_ids,
                    event: Arc::new(event),
                })
            }
            "chat_message_created" => {
                let payload: ChatMessageCreated = serde_json::from_str(payload)?;
                let users_id: HashSet<u64> = payload.members.iter().map(|v| *v as u64).collect();
                Ok(Self{
                    users_ids: users_id,
                    event: Arc::new(AppEvent::NewMessage(payload.message)),
                })
            }
            _ => Err(anyhow::anyhow!("Invalid notification type")),
        }
    }
}


fn get_affected_user_ids(old: Option<&Chat>,new: Option<&Chat>) -> HashSet<u64>{
    let mut user_ids = HashSet::new();
    
    user_ids
}